﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Core;
using Nvigorate.Aspect;
using Nvigorate.Relational.Provider;
using Nvigorate.Relational.State;

namespace Nvigorate.Demo.Domain
{
    public class DomainModule : StandardModule
    {
        public override void Load()
        {
            Bind(typeof(IList<>)).To(typeof(StatefulList<>));
        }
    }

    public class DomainFactory
    {
        private static IKernel _kernel;

        private static IKernel Kernel
        {
            get
            {
                _kernel = _kernel ?? new StandardKernel(new DomainModule());
                return _kernel;
            }
        }

        public static T Get<T>()
        {
            return Kernel.Get<T>();
        }
    }

    public class DemoBase : StateTracking
    {
        public long Id { get; private set; }
    }

    [StateTrackingAspect]
    public class Customer : DemoBase
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public PhoneNumber PrimaryPhone { get; set; }
        public PhoneNumber SecondaryPhone { get; set; }
        public EmailAddress PrimaryEmail { get; set; }
        public EmailAddress SecondaryEmail { get; set; }

        public Address MainAddress { get; set; }
        public IList<IVehicle> Vehicles { get; set; }

        public Customer()
        {
            Vehicles = DomainFactory.Get<IList<IVehicle>>();
        }
    }

    [StateTrackingAspect]
    public class Address : DemoBase
    {
        public List<Customer> Residents { get; set; }

        public string Street1 { get; set; }
        public string Street2 { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
    }

    public struct PhoneNumber
    {
        private bool _valid;
        private string _country;
        private string _area;
        private string _exchange;
        private string _number;
        private string _extension;

        public string Country 
        { 
            get { return _country; }
            private set { _country = value; } 
        }
        public string Area
        {
            get { return _area; }
            private set { _area = value; }
        }
        public string Exchange
        {
            get { return _exchange; }
            private set { _exchange = value; }
        }
        public string Number
        {
            get { return _number; }
            private set { _number = value; }
        }
        public string Extension
        {
            get { return _extension; }
            private set { _extension = value; }
        }

        public PhoneNumber(string country, string area, string exchange, string number, string extension)
        {
            _country = country;
            _area = area;
            _exchange = exchange;
            _number = number;
            _extension = extension;
            _valid = true;
        }

        public override string ToString()
        {
            if (!_valid)
                return "";

            var format = "{0}-{1}-{2}";
            var sections = new List<string>
                               {
                                   Area,
                                   Exchange,
                                   Number
                               };

            if(!string.IsNullOrEmpty(Country))
            {
                sections.Add(Country);
                format = string.Concat("{3}-", format);
            }

            if(!string.IsNullOrEmpty(Extension))
            {
                sections.Add(Extension);
                format = string.Concat(format, sections.Count == 3 ? " ext. {3}" : " ext. {4}");
            }

            return string.Format(format, sections);
        }

        public override bool Equals(object obj)
        {
            if(!_valid)
                return false;

            var candidate = (PhoneNumber) obj;
            return
                candidate.Country.Equals(Country) &&
                candidate.Country.Equals(Area) &&
                candidate.Country.Equals(Exchange) &&
                candidate.Country.Equals(Number) &&
                candidate.Country.Equals(Extension);
        }
    }

    public struct EmailAddress
    {
        private bool _valid;
        private string _account;
        private string _domainName;

        public string Account
        { 
            get { return _account; }
            private set { _account = value; }
        }

        public string Domain
        { 
            get { return _domainName; }
            private set { _domainName = value; }
        }

        public EmailAddress(string emailAddress)
        {
            var parts = emailAddress.Split('@');
            _account = parts[0];
            _domainName = parts[1];
            _valid = true;
        }

        public override string ToString()
        {
            return string.Format("{0}@{1}", _account, _domainName);
        }

        public override bool Equals(object obj)
        {
            if(!_valid)
                return false;

            var candidate = (EmailAddress) obj;
            return
                candidate.Account.Equals(Account) &&
                candidate.Domain.Equals(Domain);
        }

    }

    public interface IVehicle
    {
        IList<Customer> Drivers { get; set; }
        long Id { get; }
        VehicleType Type { get; set; }
        string Make { get; set; }
        string Model { get; set; }
        int Year { get; set; }
        string Color { get; set; }
    }

    public enum VehicleType
    {
        Car = 1,
        Truck = 2
    }

    public abstract class Vehicle : DemoBase, IVehicle
    {
        public virtual IList<Customer> Drivers { get; set; }
        public VehicleType Type { get; set; }
        public virtual string Make { get; set; }
        public virtual string Model { get; set; }
        public virtual int Year { get; set; }
        public virtual string Color { get; set; }
        public virtual VehicleType VehicleType { get; set; }

        public Vehicle()
        {
            Drivers = DomainFactory.Get <IList<Customer>>();
        }
    }

    [StateTrackingAspect]
    public class Car : Vehicle
    {
        
    }

    [StateTrackingAspect]
    public class Truck : Vehicle
    {

    }

    [StateTrackingAspect]
    public class Appointment : DemoBase
    {
        public Customer Customer { get; set; }
        public IVehicle Vehicle { get; set; }
        public Bill Bill { get; set; }

        public DateTime Time { get; set; }
    }

    [StateTrackingAspect]
    public class Bill : DemoBase
    {
        public IList<LineItem> LineItems { get; set; }
        public Appointment Appointment { get; set; }

        public Bill()
        {
            LineItems = DomainFactory.Get<IList<LineItem>>();
        }
    }
    
    public abstract class LineItem : DemoBase
    {
        public Bill Bill { get; set; }
        public decimal Units { get; set; }
    }

    public class ServiceLineItem : LineItem
    {
        public Service ServicePerformed { get; set; }
    }

    public class ProductLineItem : LineItem
    {
        public Product ProductUsed { get; set; }
    }

    public abstract class HasCostBase : DemoBase
    {
        public string Name { get; set; }
        public decimal Cost { get; set; }
    }

    [StateTrackingAspect]
    public class Service : HasCostBase
    {
    }

    [StateTrackingAspect]
    public class Product : HasCostBase
    {
    }
}
